package de.jmda.gen.java.naming;

import static de.jmda.core.mproc.ProcessingUtilities.getFields;
import static de.jmda.core.mproc.ProcessingUtilities.getTypeElement;
import static de.jmda.core.mproc.ProcessingUtilities.isProcessingOver;
import static de.jmda.core.util.CollectionsUtil.asSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;

import org.junit.Assert;
import org.junit.Test;

import de.jmda.core.mproc.task.AbstractTypeElementsTask;
import de.jmda.core.mproc.task.AbstractTypeElementsTaskTypes;
import de.jmda.core.mproc.task.TaskException;
import de.jmda.core.mproc.task.TaskRunner;

public class JUTImportManager
{
	@Test
	public void testGenerateImportStatementsEmptyForNoUseCalls()
	{
		ImportManager importManager =
				new ImportManager(
						getClass().getPackage().getName(), getClass().getSimpleName());
		
		assertEquals("", importManager.generateImportStatements().toString());
	}

	@Test
	public void testGenerateImportStatementsEmptyForUnnecessaryImports()
	{
		ImportManager importManager =
				new ImportManager(
						getClass().getPackage().getName(), getClass().getSimpleName());
		
		importManager.useType(String.class.getName()); // no import for java.lang
		importManager.useType(getClass().getName());   // no import for same package

		assertEquals("", importManager.generateImportStatements().toString());
	}

	@Test
	public void testGenerateImportStatements()
	{
		String packageName = getClass().getPackage().getName();
		
		ImportManager importManager =
				new ImportManager(packageName, getClass().getSimpleName());
		
		String qualifiedTypeName = packageName + ".test.Test";
		
		importManager.useType(qualifiedTypeName + "1");
		importManager.useType(qualifiedTypeName + "2");
		
		assertTrue(
				importManager.generateImportStatements()
				             .indexOf(qualifiedTypeName + "1") > -1);
		
		assertTrue(
				importManager.generateImportStatements()
				             .indexOf(qualifiedTypeName + "2") > -1);
	}

	@Test
	public void testUseType()
	{
		String packageName = getClass().getPackage().getName();
		
		ImportManager importManager =
				new ImportManager(packageName, getClass().getSimpleName());
		
		String qualifiedTypeName = getClass().getName();

		assertEquals(
				getClass().getSimpleName(),
				importManager.useType(qualifiedTypeName).toString());
	}
	
	@Test
	public void testAddStaticImport()
	{
		String staticImport = Assert.class.getName() + ".assertTrue";
		
		ImportManager importManager = new ImportManager("some.package", "SomeType");
		
		importManager.addStaticImport(staticImport);

		assertTrue(
				importManager.generateImportStatements()
				             .indexOf(staticImport) > -1);
	}

	@Test
	public void testAddStaticImportWithWildcardImportAlreadyExisting()
	{
		String staticImport = Assert.class.getName() + ".assertTrue";
		
		ImportManager importManager = new ImportManager("some.package", "SomeType");
		
		importManager.addStaticImport(Assert.class.getName() + ".*");
		importManager.addStaticImport(staticImport);

		assertTrue(
				importManager.generateImportStatements()
				             .indexOf(staticImport) < 0);
		assertTrue(
				importManager.generateImportStatements()
				             .indexOf(Assert.class.getName() + ".*") > 0);
	}
	
	@Test
	public void testSandbox()
	{
		ImportManager importManager =
				new ImportManager("some.target.package", "SomeTargetType");

		String type     = importManager.useType("some.target.package.Type");
		String utilDate = importManager.useType(java.util.Date.class);
		String sqlDate  = importManager.useType(java.sql.Date.class);
		String string   = importManager.useType(String.class);

		System.out.println(
				"import statements:\n" + importManager.generateImportStatements());

		System.out.println("type     [" + type     + "]");
		System.out.println("utilDate [" + utilDate + "]");
		System.out.println("sqlDate  [" + sqlDate  + "]");
		System.out.println("string   [" + string   + "]");
	}

	/**
	 * Starts an annotation processor that will process {@link
	 * TestUseTypeOfIntFieldTask} itself.
	 */
	private class TestUseTypeOfIntFieldTask extends AbstractTypeElementsTaskTypes
	{
		@SuppressWarnings("unused")
    private int intField;

		private TestUseTypeOfIntFieldTask()
		{
			super(asSet(TestUseTypeOfIntFieldTask.class));
		}

		@Override public boolean execute() throws TaskException
		{
			if (false == isProcessingOver())
			{
				TypeElement type = getTypeElements().iterator().next();
				VariableElement field = getFields(type).get(0);

				ImportManager importManager = new ImportManager();

				assertEquals("int", importManager.useTypeOf(field));
			}

			return false;
		}
	}

	@Test
	public void testUseTypeOfIntField() throws IOException
	{
		AbstractTypeElementsTask task = new TestUseTypeOfIntFieldTask();
		TaskRunner.run(task);
	}
	
	private class DomainModelType
	{
		@SuppressWarnings("unused")
    private List<DomainModelType> domainModelTypeItems;
	}

	/**
	 * Starts an annotation processor that will process {@link DomainModelType}.
	 */
	private class DomainModelTypeTask extends AbstractTypeElementsTaskTypes
	{
		private DomainModelTypeTask()
		{
			super(asSet(DomainModelType.class));
		}

		@Override public boolean execute() throws TaskException
		{
			String targetPackageName =
					DomainModelType.class.getPackage().getName();
			String targetTypeName =
					targetPackageName + "." + DomainModelType.class.getSimpleName();

			Map<TypeElement, String> typeToTargetTypeName = new HashMap<>();

			typeToTargetTypeName.put(
					getTypeElement(DomainModelType.class), targetTypeName);

			if (false == isProcessingOver())
			{
				TypeElement type = getTypeElements().iterator().next();
				VariableElement field = getFields(type).get(0);

				ImportManager importManager =
						new ImportManager(
								targetPackageName,
								DomainModelType.class.getSimpleName(),
								typeToTargetTypeName);

				String useTypeOf = importManager.useTypeOf(field);
				
				System.out.println(useTypeOf);
				System.out.println(importManager.generateImportStatements());

				assertEquals(
						"List<" + DomainModelType.class.getSimpleName() + ">", useTypeOf);
			}

			return false;
		}
	}

	@Test
	public void testUseTypeOfWithParameterisedType() throws IOException
	{
		AbstractTypeElementsTask task = new DomainModelTypeTask();
		TaskRunner.run(task);
	}
}